package com.kevin.v2.spring.formework.aop.intercept;

import com.kevin.v2.spring.formework.aop.aspect.KvAbstractAspectAdvice;
import com.kevin.v2.spring.formework.aop.aspect.KvJoinPoint;
import com.sun.org.apache.xpath.internal.operations.Or;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * 具体的执行类
 */
public class KvMethodInvocation implements KvJoinPoint {
   //代理类
   private Object proxy;
   //被通知的方法
   private Method method;
   //原声对象
   private Object target;

   private Class<?> targetClass;
   //调用方法的参数
   private Object[] args;
   //通知链
   private List<Object> interceptorsAndDynamicMethodMatchers;

   //计数器
   private int currentInterceptorIndex = -1;


   public KvMethodInvocation (Object proxy, Method method, Object target, Class<?> targetClass, Object[] args, List<Object> interceptorsAndDynamicMethodMatchers) {
      this.proxy = proxy;
      this.method = method;
      this.target = target;
      this.targetClass = targetClass;
      this.args = args;
      this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
   }

   public Object proceed () {
      try {
         //如果没有通知 直接执行原声方法
         if (this.currentInterceptorIndex == interceptorsAndDynamicMethodMatchers.size() - 1) {
            return this.method.invoke(target, args);
         }

         //拿到通知对象
         Object interceptorOrInterceptorAdvice = interceptorsAndDynamicMethodMatchers.get(++currentInterceptorIndex);
         if (interceptorOrInterceptorAdvice instanceof KvMethodInterceptor) {
            KvMethodInterceptor kvMethodInterceptor = (KvMethodInterceptor) interceptorOrInterceptorAdvice;
            return kvMethodInterceptor.invoke(this);
         } else proceed(); //递归顺序执行

      } catch (Exception e) {
         e.printStackTrace();
      } catch (Throwable throwable) {
         throwable.printStackTrace();
      }
      return null;
   }


   @Override
   public Method getMethod () {
      return this.method;
   }

   @Override
   public Object[] getArgs () {
      return this.args;
   }

   @Override
   public Object getThis () {
      return this.target;
   }
}
